package ru.amse.tsyganov.jumleditor.main;

import java.io.File;
import java.io.IOException;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;

import ru.amse.tsyganov.jumleditor.commands.CommandActionListener;
import ru.amse.tsyganov.jumleditor.commands.ViewsManager;
import ru.amse.tsyganov.jumleditor.graphicaleditor.GraphicalEditor;
import ru.amse.tsyganov.jumleditor.graphicaleditor.ItemsSelectionListener;
import ru.amse.tsyganov.jumleditor.model.common.Edge;
import ru.amse.tsyganov.jumleditor.model.common.GraphModel;
import ru.amse.tsyganov.jumleditor.model.common.State;
import ru.amse.tsyganov.jumleditor.properties.PropertiesTable;
import ru.amse.tsyganov.jumleditor.storing.Storage;
import ru.amse.tsyganov.jumleditor.storing.StoreException;
import ru.amse.tsyganov.jumleditor.validator.Problem;
import ru.amse.tsyganov.jumleditor.validator.ProblemSelectionListener;
import ru.amse.tsyganov.jumleditor.validator.ProblemsList;
import ru.amse.tsyganov.jumleditor.validator.ProblemsTable;
import ru.amse.tsyganov.jumleditor.validator.Validator;
import ru.amse.tsyganov.jumleditor.view.ModelChangeListener;
import ru.amse.tsyganov.jumleditor.view.SelectedItems;
import ru.amse.tsyganov.jumleditor.view.common.EdgeView;
import ru.amse.tsyganov.jumleditor.view.common.GraphView;
import ru.amse.tsyganov.jumleditor.view.common.StateView;

public class Editor {
	
	class ActivityGraphChangeListener implements ModelChangeListener {
		
		public void modelChanged(GraphModel graphModel) {
			revalidate();
		}
		
	}
	
	private static final String SHELL_CAPTION = "JUmlEditor";
	
	private static final int INITIAL_SHELL_WIDTH = 700;
	
	private static final int INITIAL_SHELL_HEIGHT = 700;
	
	private final Display display;
	
	private final Shell sShell;
	
	private final GraphicalEditor graphicalEditor;
	
	private final ViewsManager viewsManager;
	
	private final TabFolder tabFolder; 
	
	private final PropertiesTable propertiesTable;
	
	private final ProblemsTable problemsTable;
	
	private final SashForm composite;
	
	private final Menu menu;
	
	private ChartType currentChartType = ChartType.NONE;
	
	private final ItemsSelectionListener itemsSelectionListener = 
		new ItemsSelectionListener() {

			public void selectedItemsChanged(SelectedItems selectedItems) {
				
				deleteMenuItem.setEnabled(
						selectedItems.getSelectedStates().size() +
						selectedItems.getSelectedTransitions().size() > 0);
				
				problemsTable.getTable().deselectAll();
				
				if (selectedItems.selectedStatesSize() + 
						selectedItems.selectedTransitionsSize() == 1) {
									
					if (selectedItems.selectedStatesSize() == 1) {
						StateView<? extends State> selectedState = 
							selectedItems.getSelectedStates().
								iterator().next();
						
						propertiesTable.setSource(selectedState);
						
						int i = 0;
						for (Problem p : problemsTable.getProblemsList().getList()) {
							if (p.getProblemPlace().selectedStatesContains(selectedState)) {
								problemsTable.getTable().select(i);
							}
							i++;
						}	
					}
					
					if (selectedItems.selectedTransitionsSize() == 1) {
						EdgeView<? extends Edge> selectedTransition =
							selectedItems.getSelectedTransitions().
								iterator().next();
						
						propertiesTable.setSource(selectedTransition);
						
						int i = 0;
						for (Problem p : problemsTable.getProblemsList().getList()) {
							if (p.getProblemPlace().selectedTransitionsContains(selectedTransition)) {
								problemsTable.getTable().select(i);
							}
							i++;
						}
					}
					
					problemsTable.getTable().redraw();
				} else {
					propertiesTable.clear();
				}
			}
		
		};
		
	private final ProblemSelectionListener problemSelectionListener = 
		new ProblemSelectionListener() {

		public void problemSelected(Problem p) {
			graphicalEditor.setSelectedItems(p.getProblemPlace());
		}
		
	};
		
	private String fileName = null;

	private MenuItem deleteMenuItem;

	private MenuItem undoMenuItem;

	private MenuItem redoMenuItem;
	
	public Editor(GraphView<GraphModel> activityGraphView) {
				
		display = new Display();
		
		sShell = new Shell(display);
		
		final int VIEWS_MANAGER_SIZE = 20; 
		viewsManager = new ViewsManager(VIEWS_MANAGER_SIZE);
		
		viewsManager.addListener(new CommandActionListener() {

			public void action() {
				undoMenuItem.setEnabled(viewsManager.canUndo());
				redoMenuItem.setEnabled(viewsManager.canRedo());
				if (viewsManager.canUndo() && fileName != null) {
					sShell.setText(SHELL_CAPTION + " - " + fileName + "*");
				} else if (fileName != null) {
					sShell.setText(SHELL_CAPTION + " - " + fileName);
				}
			}
			
		});
		
		composite = new SashForm(sShell, SWT.VERTICAL);
				
		graphicalEditor = new GraphicalEditor(composite, activityGraphView, viewsManager);
		
		tabFolder = new TabFolder(composite, SWT.NONE);
		
		composite.setWeights(new int[] {3, 1});
		
		propertiesTable = new PropertiesTable(tabFolder, 
				INITIAL_SHELL_WIDTH - 20, viewsManager);
		
		problemsTable = new ProblemsTable(tabFolder, INITIAL_SHELL_WIDTH - 20);
		
		menu = new Menu(sShell, SWT.BAR);
		
		sShell.setMenuBar(menu);
		
		initMenu();
		
		createSShell();
				
		bindActivityGraphView(activityGraphView);
		
		sShell.open();
		StartDialog startDialog = new StartDialog(sShell);
		startDialog.open();
		switch (startDialog.getStatus()) {
		case StartDialog.CANCEL:
			break;
		case StartDialog.OPEN_CHART:
			open();
			break;
		case StartDialog.NEW_ACTIVITY_CHART:
			newActivityChart();
			break;
		case StartDialog.NEW_COLLABORATION_CHART:
			newCollaborationChart();
			break;
		}
		openAndWaitEvents();
	}

	private void bindActivityGraphView(GraphView<GraphModel> activityGraphView) {
		
		activityGraphView.getModel().addModelChangeListener(
				new ActivityGraphChangeListener());
		
		ProblemsList probems = Validator.validate(activityGraphView, currentChartType);
		probems.addProblemSelectionListener(problemSelectionListener);
		problemsTable.setSource(probems);
		
		graphicalEditor.addSelectionListener(itemsSelectionListener);
	}

	private void openAndWaitEvents() {
		while(!sShell.isDisposed()) {
		   if(!display.readAndDispatch()) {
		       display.sleep();
		   }
		}
		display.dispose();
	}

	private void createSShell() {
		
		sShell.setText(SHELL_CAPTION);
		sShell.setImage(currentChartType.getImage());
		
		initPropertiesTable();
		initProblemsTable();
		
		sShell.setLayout(new FillLayout());
				
		sShell.setSize(new Point(INITIAL_SHELL_WIDTH, 
				INITIAL_SHELL_HEIGHT));
		
		assert sShell != null;
	}

	private void initPropertiesTable() {
		assert sShell != null;
		assert propertiesTable != null;

		TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
		tabItem.setControl(propertiesTable.getTable());
		tabItem.setText("Properties");	
	}
	
	private void initProblemsTable() {
		assert sShell != null;
		assert problemsTable != null;
		
		TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
		tabItem.setControl(problemsTable.getTable());
		tabItem.setText("Problems");
	}
	
	private void initMenu() {
		MenuItem fileMenuItem = new MenuItem(menu, SWT.CASCADE);
		fileMenuItem.setText("&File");
		
		Menu fileMenu = new Menu(sShell, SWT.DROP_DOWN);
		fileMenuItem.setMenu(fileMenu);
		
		MenuItem newMenuItem = new MenuItem(fileMenu, SWT.CASCADE);
		newMenuItem.setText("&New");
		
		Menu newMenu = new Menu(sShell, SWT.DROP_DOWN);
		newMenuItem.setMenu(newMenu);
		
		MenuItem newActivityChartMenuItem = new MenuItem(newMenu, SWT.PUSH);
		newActivityChartMenuItem.setText("&New activity UML chart");
		newActivityChartMenuItem.setImage(ChartType.ACTIVITY.getImage());
		
		MenuItem newCollaborationChartMenuItem = new MenuItem(newMenu, SWT.PUSH);
		newCollaborationChartMenuItem.setText("&New collaboration UML chart");
		newCollaborationChartMenuItem.setImage(ChartType.COLLABORATION.getImage());
		
		MenuItem openMenuItem = new MenuItem(fileMenu, SWT.PUSH);
		openMenuItem.setText("&Open\tCtrl+O");
		openMenuItem.setAccelerator(SWT.CTRL + 'O');
		
		new MenuItem(fileMenu, SWT.SEPARATOR);
		
		MenuItem saveMenuItem = new MenuItem(fileMenu, SWT.PUSH);
		saveMenuItem.setText("&Save\tCtrl+S");
		saveMenuItem.setAccelerator(SWT.CTRL + 'S');
		
		MenuItem saveAsMenuItem = new MenuItem(fileMenu, SWT.PUSH);
		saveAsMenuItem.setText("Save &As...");
		
		new MenuItem(fileMenu, SWT.SEPARATOR);
		
		MenuItem exitMenuItem = new MenuItem(fileMenu, SWT.PUSH);
		exitMenuItem.setText("E&xit\tCtrl+X");
		exitMenuItem.setAccelerator(SWT.CTRL + 'X');
		
		exitMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				if (viewsManager.canUndo()) {
					if (askAboutSavingChanges()) {
						sShell.close();
					}
				} else {
					sShell.close();
				}
			}
			
		});
		
		openMenuItem.addSelectionListener(new SelectionListener() {
			
			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				if (viewsManager.canUndo()) {
					if (askAboutSavingChanges()) {
						open();
					}
				} else {
					open();
				}
			}
		});
		
		saveAsMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				FileDialog fileDialog = new FileDialog(sShell, SWT.SAVE);
				fileDialog.setFilterExtensions(currentChartType.allowedExtensions());
				String newFileName = fileDialog.open();
				askAndSave(newFileName);
				sShell.setImage(currentChartType.getImage());
				initToolsAccordingToChart();
			}
			
		});
		
		saveMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				save();
			}
			
		});
		
		newActivityChartMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				if (viewsManager.canUndo()) {
					if (askAboutSavingChanges()) {
						newActivityChart();
					}
				} else {
					newActivityChart();
				}
			}			
		});
		
		newCollaborationChartMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				if (viewsManager.canUndo()) {
					if (askAboutSavingChanges()) {
						newCollaborationChart();
					}
				} else {
					newCollaborationChart();
				}
			}
			
		});
		
		MenuItem editMenuItem = new MenuItem(menu, SWT.CASCADE);
		editMenuItem.setText("&Edit");
		
		Menu editMenu = new Menu(sShell, SWT.DROP_DOWN);
		editMenuItem.setMenu(editMenu);
		
		deleteMenuItem = new MenuItem(editMenu, SWT.PUSH);
		deleteMenuItem.setText("&Delete\tDelete");
		deleteMenuItem.setAccelerator(SWT.DEL);
		deleteMenuItem.setEnabled(false);
		
		new MenuItem(editMenu, SWT.SEPARATOR);
		
		undoMenuItem = new MenuItem(editMenu, SWT.PUSH);
		undoMenuItem.setText("&Undo\tCtrl+Z");
		undoMenuItem.setAccelerator(SWT.CTRL + 'Z');
		undoMenuItem.setEnabled(false);
		
		redoMenuItem = new MenuItem(editMenu, SWT.PUSH);
		redoMenuItem.setText("&Redo\tCtrl+Y");
		redoMenuItem.setAccelerator(SWT.CTRL + 'Y');
		redoMenuItem.setEnabled(false);
		
		deleteMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				graphicalEditor.deleteSelectedItems();
			}
			
		});
		
		undoMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				graphicalEditor.undo();
			}
			
		});
		
		redoMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				graphicalEditor.redo();
			}
			
		});
		
		MenuItem helpMenuItem = new MenuItem(menu, SWT.CASCADE);
		helpMenuItem.setText("&Help");
		
		Menu helpMenu = new Menu(sShell, SWT.DROP_DOWN);
		helpMenuItem.setMenu(helpMenu);
		
		MenuItem aboutMenuItem = new MenuItem(helpMenu, SWT.PUSH);
		aboutMenuItem.setText("&About JUmlEditor");
		
		aboutMenuItem.addSelectionListener(new SelectionListener() {

			public void widgetDefaultSelected(SelectionEvent e) {
				widgetSelected(e);
			}

			public void widgetSelected(SelectionEvent e) {
				MessageDialog.openInformation(sShell, "About JUmlEditor", 
						"JUmlEditor v1.0\n\n" + 
						"Academy of modern software engeneering, 2007\n\n" +
						"Home: http://www.amse.ru/\n" +
						"Author: Dmitry Tsyganov");
			}
			
		});
	}
	
	private boolean askAboutSavingChanges() {
		MessageDialog dialog = new MessageDialog(sShell, "JUmlEditor", null,
				"Save changes ?", MessageDialog.QUESTION, 
				new String[] { 
					IDialogConstants.YES_LABEL,
                    IDialogConstants.NO_LABEL,
                    IDialogConstants.CANCEL_LABEL}, 0);
		
		switch (dialog.open()) {
		case 0: return save();
		case 1:	return true;
		case 2:	return false;
		}
		return true;
	}
	
	private boolean save() {
		boolean result;
		if (fileName == null) {
			FileDialog fileDialog = new FileDialog(sShell, SWT.SAVE);
			fileDialog.setFilterExtensions(currentChartType.allowedExtensions());
			String newFileName = fileDialog.open();
			result = askAndSave(newFileName);
		} else {
			saveFile();
			result = true;
		}
		sShell.setImage(currentChartType.getImage());
		initToolsAccordingToChart();
		return result;
	}
	
	private boolean askAndSave(String newFileName) {
		
		if (newFileName == null) {
			return false;
		} 
		
		if (new File(newFileName).exists()) {
			if (MessageDialog.openQuestion(sShell, "JUmlEditor", 
				"File already exists. Replace?")) {
		
				fileName = newFileName;
				saveFile();
				sShell.setText(SHELL_CAPTION + " - " + fileName);
				currentChartType = ChartType.resolveType(fileName);
				return true;
			}
		} else {
			fileName = newFileName;
			saveFile();
			sShell.setText(SHELL_CAPTION + " - " + fileName);
			currentChartType = ChartType.resolveType(fileName);
			return true;
		}
		return false;
	}
	
	private void showErrorMessage(final Exception e) {
		new ErrorMessage(sShell, e).open();
	}
	
	private boolean saveFile() {
		assert fileName != null;
		try {
			Storage.write(graphicalEditor.getActivityGraphView(), fileName);
			viewsManager.clear();
			return true;
		} catch (IOException e) {
			showErrorMessage(e);
		} catch (StoreException e) {
			showErrorMessage(e);
		}
		return false;
	}

	private void initToolsAccordingToChart() {
		switch (currentChartType) {
		case COLLABORATION : 
			graphicalEditor.initCollaborationChartTools();
		break;
		case ACTIVITY : 
			graphicalEditor.initActivityChartTools();
		break;
		}
	}

	private void open() {
		try {
			FileDialog fileDialog = new FileDialog(sShell, SWT.OPEN);
			fileDialog.setFilterExtensions(ChartType.NONE.allowedExtensions());
			String newFileName = fileDialog.open();
			
			if (newFileName == null) {
				return;
			}
			
			fileName = newFileName;
			graphicalEditor.getActivityGraphView().clear();
			Storage.read(newFileName, graphicalEditor.getActivityGraphView(),
					graphicalEditor.getPropertyChangeListener());
			sShell.setText(SHELL_CAPTION + " - " + fileName);
			viewsManager.clear();
			graphicalEditor.getCanvas().redraw();
			currentChartType = ChartType.resolveType(fileName);
			initToolsAccordingToChart();
		} catch (IOException e1) {
			showErrorMessage(e1);
		} catch (StoreException e1) {
			showErrorMessage(e1);
		}
		sShell.setImage(currentChartType.getImage());
		revalidate();
	}
	
	private void newActivityChart() {
		fileName = null;
		
		graphicalEditor.getActivityGraphView().clear();
		graphicalEditor.getCanvas().redraw();
		sShell.setText(SHELL_CAPTION);
		graphicalEditor.initActivityChartTools();
		currentChartType = ChartType.ACTIVITY;
		sShell.setImage(currentChartType.getImage());
		revalidate();
		viewsManager.clear();
	}
	
	private void newCollaborationChart() {
		fileName = null;
		
		graphicalEditor.getActivityGraphView().clear();
		graphicalEditor.getCanvas().redraw();
		sShell.setText(SHELL_CAPTION);
		graphicalEditor.initCollaborationChartTools();
		currentChartType = ChartType.COLLABORATION;
		sShell.setImage(currentChartType.getImage());
		revalidate();
		viewsManager.clear();
	}
	
	private void revalidate() {
		ProblemsList probems = Validator.validate(graphicalEditor.getActivityGraphView(), currentChartType);
		probems.addProblemSelectionListener(problemSelectionListener);
		problemsTable.setSource(probems);
	}
	public ChartType getCurrentChartType() {
		return currentChartType;
	}
}
